﻿using Castle.Core.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WJP.Queue.Scheduling
{
    public class ScheduleService : IScheduleService
    {
        private readonly object _lockObject = new object();
        private readonly Dictionary<string, TimerBasedTask> _taskDict = new Dictionary<string, TimerBasedTask>();
        private ILogger Logger { get; set; }

        /// <summary>
        /// 启动一个任务
        /// </summary>
        /// <param name="name">任务名称</param>
        /// <param name="action">任务执行方法</param>
        /// <param name="dueTime">延迟之前调用的回调方法时指定的时间量</param>
        /// <param name="period">回调方法的调用之间的时间间隔</param>
        public void StartTask(string name, Action action, int dueTime, int period)
        {
            lock (_lockObject)
            {
                if (_taskDict.ContainsKey(name))
                    return;

                var timer = new Timer(TaskCallback, name, Timeout.Infinite, Timeout.Infinite);
                _taskDict.Add(name, new TimerBasedTask
                {
                    Name = name,
                    Action = action, 
                    Timer = timer,
                    DueTime = dueTime,
                    Period = period,
                    Stopped = false
                });
                timer.Change(dueTime, period);
            }
        }

        /// <summary>
        /// 停止一个任务
        /// </summary>
        /// <param name="name"></param>
        public void StopTask(string name)
        {
            lock (_lockObject)
            {
                if (_taskDict.ContainsKey(name))
                {
                    var task = _taskDict[name];
                    task.Stopped = true;
                    task.Timer.Dispose();
                    _taskDict.Remove(name);
                }
            }
        }

        /// <summary>
        /// 任务回调方法
        /// </summary>
        /// <param name="obj"></param>
        private void TaskCallback(object obj)
        {
            var taskName = (string)obj;
            TimerBasedTask task;

            if(_taskDict.TryGetValue(taskName, out task))
            {
                try
                {
                    if (!task.Stopped)
                    {
                        task.Timer.Change(Timeout.Infinite, Timeout.Infinite);
                        task.Action();
                    }
                }
                catch (ObjectDisposedException){ }
                catch (Exception ex)
                {
                    Logger?.Error("任务异常，任务名称：{0}，due：{1}，period：{2}".FormatArgs(task.Name, task.DueTime, task.Period), ex);
                }
                finally
                {
                    try
                    {
                        if (!task.Stopped)
                        {
                            task.Timer.Change(task.DueTime, task.Period);
                        }
                    }
                    catch (ObjectDisposedException) { }
                    catch (Exception ex)
                    {
                        Logger?.Error("任务异常，任务名称：{0}，due：{1}，period：{2}".FormatArgs(task.Name, task.DueTime, task.Period), ex);
                    }
                }
            }
        }
    }

    /// <summary>
    /// 定时器基础信息
    /// </summary>
    class TimerBasedTask
    {
        public string Name;
        public Action Action;
        public Timer Timer;
        public int DueTime;
        public int Period;
        public bool Stopped;
    }
}
